package top.ochiamalu.aioj.controller;

import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.ochiamalu.aioj.common.BaseResponse;
import top.ochiamalu.aioj.common.ErrorCode;
import top.ochiamalu.aioj.properties.GiteeProperties;
import top.ochiamalu.aioj.properties.GithubProperties;
import top.ochiamalu.aioj.constant.AuthConstants;
import top.ochiamalu.aioj.constant.RedirectConstants;
import top.ochiamalu.aioj.enums.OAuthOperationEnum;
import top.ochiamalu.aioj.oauth.OAuthCallbackHandler;
import top.ochiamalu.aioj.properties.OAuthProperties;
import top.ochiamalu.aioj.service.OAuthService;
import top.ochiamalu.aioj.util.OAuthStateUtils;
import top.ochiamalu.aioj.util.ResultUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 认证控制器
 *
 * @author ochiamalu
 * @date 2025/03/12
 */
@RestController
@RequestMapping("/oauth")
public class OAuthController {

    @Resource
    private OAuthService oAuthService;

    @Resource
    private OAuthProperties oAuthProperties;

    @Resource
    private GithubProperties githubProperties;

    @Resource
    private GiteeProperties giteeProperties;

    @Resource
    private ApplicationContext applicationContext;

    /**
     * 获取github认证网址
     *
     * @param type 操作类型
     * @return {@link BaseResponse}<{@link String}>
     */
    @GetMapping("/github/url")
    public BaseResponse<String> getGithubAuthUrl(@RequestParam("type") String type) {
        OAuthOperationEnum operationEnum = OAuthOperationEnum.getByValue(type);
        if (operationEnum == null) {
            return ResultUtils.error(ErrorCode.PARAMS_ERROR, "无效的操作类型");
        }

        // 绑定操作需要登录
        if (operationEnum == OAuthOperationEnum.BIND && !StpUtil.isLogin()) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }

        String state = OAuthStateUtils.generateState(type);
        String url = String.format(AuthConstants.GITHUB_AUTH_URL,
                githubProperties.getClientId(), githubProperties.getRedirectUri(), state);

        return ResultUtils.success(url);
    }

    /**
     * 获取gitee认证网址
     *
     * @param type 操作类型
     * @return {@link BaseResponse}<{@link String}>
     */
    @GetMapping("/gitee/url")
    public BaseResponse<String> getGiteeAuthUrl(@RequestParam("type") String type) {
        OAuthOperationEnum operationEnum = OAuthOperationEnum.getByValue(type);
        if (operationEnum == null) {
            return ResultUtils.error(ErrorCode.PARAMS_ERROR, "无效的操作类型");
        }

        // 绑定操作需要登录
        if (operationEnum == OAuthOperationEnum.BIND && !StpUtil.isLogin()) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }

        String state = OAuthStateUtils.generateState(type);
        String url = String.format(AuthConstants.GITEE_AUTH_URL,
                giteeProperties.getClientId(), giteeProperties.getRedirectUri(), state);

        return ResultUtils.success(url);
    }

    /**
     * github回调
     *
     * @param code     代码
     * @param state    状态
     * @param response 响应
     * @throws IOException IOException
     */
    @GetMapping("/github/callback")
    public void githubCallback(
            @RequestParam("code") String code,
            @RequestParam("state") String state,
            HttpServletResponse response) throws IOException {
        handleCallback("github", code, state, response);
    }

    /**
     * gitee回调
     *
     * @param code     代码
     * @param state    状态
     * @param response 响应
     * @throws IOException IOException
     */
    @GetMapping("/gitee/callback")
    public void giteeCallback(
            @RequestParam("code") String code,
            @RequestParam("state") String state,
            HttpServletResponse response) throws IOException {
        handleCallback("gitee", code, state, response);
    }

    /**
     * 获得gitee绑定状态
     *
     * @return {@link BaseResponse }<{@link Boolean }>
     */
    @GetMapping("/gitee/bind/status")
    public BaseResponse<Boolean> getGiteeBindStatus() {
        if (!StpUtil.isLogin()) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }
        long userId = StpUtil.getLoginIdAsLong();
        return ResultUtils.success(oAuthService.isGiteeBound(userId));
    }

    /**
     * 取消绑定github
     *
     * @return {@link BaseResponse }<{@link Boolean }>
     */
    @PostMapping("/github/unbind")
    public BaseResponse<Boolean> unbindGithub() {
        if (!StpUtil.isLogin()) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }
        long userId = StpUtil.getLoginIdAsLong();
        return ResultUtils.success(oAuthService.unbindGithub(userId));
    }

    /**
     * 解除绑定gitee
     *
     * @return {@link BaseResponse }<{@link Boolean }>
     */
    @PostMapping("/gitee/unbind")
    public BaseResponse<Boolean> unbindGitee() {
        if (!StpUtil.isLogin()) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }
        long userId = StpUtil.getLoginIdAsLong();
        return ResultUtils.success(oAuthService.unbindGitee(userId));
    }

    /**
     * 获取github绑定状态
     *
     * @return {@link BaseResponse }<{@link Boolean }>
     */
    @GetMapping("/github/bind/status")
    public BaseResponse<Boolean> getGithubBindStatus() {
        if (!StpUtil.isLogin()) {
            return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR);
        }
        long userId = StpUtil.getLoginIdAsLong();
        return ResultUtils.success(oAuthService.isGithubBound(userId));
    }

    /**
     * 处理回调
     *
     * @param platform 平台
     * @param code     代码
     * @param state    状态
     * @param response 响应
     * @throws IOException IOException
     */
    private void handleCallback(String platform, String code, String state, HttpServletResponse response) throws IOException {
        // TODO 跳转至 error 页面
        // 验证state
        if (!OAuthStateUtils.validateState(state)) {
            response.sendRedirect(oAuthProperties.getFrontendUrl() + RedirectConstants.LOGIN_INVALID_STATE_URL);
            return;
        }

        String type = OAuthStateUtils.getStateType(state);
        OAuthOperationEnum operation = OAuthOperationEnum.getByValue(type);
        if (operation == null) {
            response.sendRedirect(oAuthProperties.getFrontendUrl() + RedirectConstants.LOGIN_INVALID_TYPE_URL);
            return;
        }

        // 如果是绑定操作，需要检查用户是否已经登录
        if (operation == OAuthOperationEnum.BIND && !StpUtil.isLogin()) {
            response.sendRedirect(oAuthProperties.getFrontendUrl() + RedirectConstants.BIND_NOT_LOGIN_URL);
            return;
        }

        try {
            // 根据操作类型获取对应的处理器
            String handlerName = platform + operation.name().charAt(0) + operation.name().substring(1).toLowerCase() + "Handler";
            OAuthCallbackHandler handler = applicationContext.getBean(handlerName, OAuthCallbackHandler.class);
            handler.handle(code, response);
        } catch (Exception e) {
            // 根据操作类型决定错误重定向页面
            if (operation == OAuthOperationEnum.LOGIN) {
                response.sendRedirect(oAuthProperties.getFrontendUrl() + RedirectConstants.LOGIN_SYSTEM_ERROR_URL);
            } else {
                response.sendRedirect(oAuthProperties.getFrontendUrl() + RedirectConstants.BIND_SYSTEM_ERROR_URL);
            }
        }
    }
}
